package com.towexin.config.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.*;

/**
 * TODO
 *
 * @author Towexin
 * @version 1.0
 * @date 2020/5/22 17:20
 */
@Configuration
public class ShiroConfig {
    @Bean
    ShiroRealm myRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(myRetryLimitCredentialsMatcher());
        return shiroRealm;
    }


    //权限管理，配置主要是Realm的管理认证
    @Bean
    public SecurityManager customizeSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm());
        securityManager.setRememberMeManager(rememberMeManager());
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 会话管理器
     * @return sessionManager
     */
    @Bean
    public SessionManager sessionManager(){
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        // 加入缓存管理器
        manager.setCacheManager(ehCacheManager());

        // 设置全局session超时时间(60分钟)
        manager.setGlobalSessionTimeout(1000 * 2 * 2);


        //是否开启删除无效的session对象  默认为true
        manager.setDeleteInvalidSessions(Boolean.TRUE);

        // 是否定时检查session
        manager.setSessionValidationSchedulerEnabled(Boolean.TRUE);
        //设置session失效的扫描时间, 清理用户直接关闭浏览器造成的孤立会话 默认为 1个小时
        //设置该属性 就不需要设置 ExecutorServiceSessionValidationScheduler 底层也是默认自动调用ExecutorServiceSessionValidationScheduler
        //暂时设置为 一分钟 用来测试
        manager.setSessionValidationInterval(1000 * 60);
        //取消url 后面的 JSESSIONID
        manager.setSessionIdUrlRewritingEnabled(Boolean.FALSE);


        // 添加session监听
        Collection<SessionListener> listeners = new ArrayList<>();
        listeners.add(sessionListener());
        manager.setSessionListeners(listeners);
        manager.setSessionIdCookieEnabled(Boolean.TRUE);
        manager.setSessionDAO(sessionDAO());

        return manager;
    }

    /**
     * cookie对象;
     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
     * @return SimpleCookie
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间7天 ,单位秒;-->
        simpleCookie.setMaxAge(604800);
        //simpleCookie.setMaxAge(300);
        //如果在Cookie中设置了"HttpOnly"属性，那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息，这样能有效的防止XSS攻击。
        simpleCookie.setHttpOnly(Boolean.TRUE);
        simpleCookie.setPath("/");
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     * @return CookieRememberMeManager
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("W/eWPtUVxxdCNn2EaizFmw=="));
        return cookieRememberMeManager;
    }

    //Filter工厂，设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");

        Map<String, Filter> filtersMap = new LinkedHashMap<>();
        // 权限认证
        filtersMap.put("perms", customizePermissionsAuthorizationFilter());
        // 角色认证
        filtersMap.put("roles", customizeRolesAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);

        Map<String, String> map = new HashMap<>();

        // 放行静态资源
        map.put("/css/**", "anon");
        map.put("/img/**", "anon");
        map.put("/plug-in/**", "anon");
        // 放行图片请求
        map.put("/file/img/**", "anon");
        // 放行webSocket
        map.put("/web-socket-communication/**", "anon");
        // 放行登录接口
        map.put("/user/login", "anon");
        map.put("/activities/**", "anon");
        map.put("/activiti/**", "anon");

        map.put("/swagger-ui/**", "perms[system.swagger]");
        //对所有请求认证
        map.put("/**", "user");

        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //错误页面，权限认证不通过跳转(已自定义配置)
        //shiroFilterFactoryBean.setUnauthorizedUrl("/page/err/404");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }


    //开启对shior注解的支持
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(customizeSecurityManager());
        return advisor;
    }

    /*
     * shiro缓存管理器;
     * 需要注入对应的其它的实体类中-->安全管理器：securityManager可见securityManager是整个shiro的核心；
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
        return ehCacheManager;
    }

    @Bean
    public SessionDAO sessionDAO() {
        EnterpriseCacheSessionDAO enterpriseCacheSessionDAO = new EnterpriseCacheSessionDAO();
        enterpriseCacheSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        return enterpriseCacheSessionDAO;
    }
    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    /**
     * 限制登录次数
     * @return 匹配器
     */
    @Bean
    public CredentialsMatcher myRetryLimitCredentialsMatcher() {
        CustomizeLimitCredentialsMatcher retryLimitCredentialsMatcher = new CustomizeLimitCredentialsMatcher();
        // 设置3次机会，默认5次
        retryLimitCredentialsMatcher.setMaxRetryNum(3);
        return retryLimitCredentialsMatcher;
    }


    /**
     * 注入自定义授权认证管理器，认证不通过进入过滤器
     */
    @Bean
    public CustomizePermissionsAuthorizationFilter customizePermissionsAuthorizationFilter(){
        //aiSellPermissionsAuthorizationFilter.setUnauthorizedUrl("/page/err/404");
        return new CustomizePermissionsAuthorizationFilter();
    }

    /**
     * 注入自定义角色认证管理器，认证不通过进入过滤器
     */
    @Bean
    public CustomizeRolesAuthorizationFilter customizeRolesAuthorizationFilter(){
        return new CustomizeRolesAuthorizationFilter();
    }

    /**
     * 在thymeleaf与Shiro结合，前端html访问Shiro时使用
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public CustomizeSessionListener sessionListener(){
        return new CustomizeSessionListener();
    }
}
